/*
=======================================================================

  Captain - BOTS
  
=======================================================================
*/
#include "g_local.h"

#include "bots_game2cgame.h"

#include "bg_local.h"
#include "bots_local.h"
#include "bots_captain.h"

// Update bitfields...etc
void bots_updateClientData_captain(gentity_t *self, bots_g2cg_captain_t *p) {
	int tmp;


	if (!self->client)
		return;

	// Warcry
	tmp = floor((self->client->iWarcryDebounce - level.time)/1000) + 1;
	p->sWarcryDelay = (tmp > 0)?tmp:0;

	// Proxies? grenade/weapon modes?
	p->weapons.gren1		= self->client->i_numProx;
	p->weapons.gren2		= 0;
	p->weapons.grenademode	= self->client->qb_proxMode;
	p->weapons.weaponmode 	= self->client->weapontoggle?self->client->i_splitmode:0;
}

// Ad hoc function as that state is not preserved anywhere. Argh.
static void bots_updateKeyDistanceCaptain(gentity_t *self, short sKeyDistance) {
	BOTS_G2CG_GETCLASSDATA(bots_g2cg_captain_t,self)->sKeyDistance = sKeyDistance;
}

void BOTS_droppromote(int clientNum, qboolean launch)
{
	gentity_t*  ent = g_entities + clientNum;
	gentity_t*	drop;
	gitem_t*	item;
	vec3_t		angles, velocity, origin, forward, right, up, muzzle, wallp;
	trace_t		tr1;
	int			i;

	if (ent->bots_class != BOTS_CAPTAIN)
	{
		BOTS_cp_console(clientNum, "Only Captains have promotion keys.");
		return;	
	}

	if (ent->promotionkey)
	{
		BOTS_cp_console(clientNum, "You don't have the promotion key.");
		return;	
	}

	bots_setCaptainLostKey(ent->client->sess.sessionTeam, qtrue);

	// set aiming directions
	AngleVectors (ent->client->ps.viewangles, forward, NULL, NULL);

	// setup muzzle point
	CalcMuzzlePoint(ent, forward, NULL, NULL, muzzle);

	if (launch)
	{
		// Setup end point of the vector we want to trace
		VectorCopy(muzzle, wallp);
		VectorMA(wallp, 50, forward, wallp);
		trap_Trace(&tr1, muzzle, NULL, NULL, wallp, ent->s.number, MASK_SOLID);
		SnapVector( tr1.endpos );

		// check to make sure we are not too close to a wall	
		if (tr1.fraction != 1)
			return;
	}

	if (ent->client->sess.sessionTeam == TEAM_RED)
		item = BG_FindItem("Red Promotion Key");
	else
		item = BG_FindItem("Blue Promotion Key");

	// set aiming directions
	VectorCopy( ent->s.apos.trBase, angles );

	forward[2] = 0;
	VectorCopy(ent->s.pos.trBase, origin);

	angles[PITCH] = 0;	// always forward

	if (launch)
	{
		AngleVectors( angles, velocity, NULL, NULL );
		VectorScale( velocity, 150, velocity );
		velocity[2] += 100;
	}
	else
		VectorSet(velocity, 0, 0, 0);

	drop = LaunchItem( item, origin, velocity );
	drop->parent = ent;
	drop->i_dropTime = level.time;

	// tilt the head to look up a bit
	AngleVectors (ent->client->ps.viewangles, NULL, right, up);
	right[2] = 0;
	VectorScale(forward, -1, forward);
	BOTS_vectorFromAngle(forward, axisDefault[2], right, 60, forward);
	vectoangles(forward, drop->s.apos.trBase);

	// set all team mates promotionkey entity to this key
	for (i = 0; i < level.maxclients; i++)
	{
		gentity_t* player = &g_entities[i];

		if (!player->client)
			continue;
		if (player->client->sess.sessionTeam == TEAM_SPECTATOR)
			continue;
		if (!OnSameTeam (ent, player))
			continue;

		player->promotionkey = drop;
	}

	// write client data to the config string
	// BOTS_CaptainConfigString(clientNum);
}

void BOTS_firesci(int clientNum)
{
	gentity_t*	ent = g_entities + clientNum;
	gentity_t*	scientist = NULL;
	int			i;

	if (!ent->client)
		return;

	// only captains can fire the scientist
	if (ent->bots_class != BOTS_CAPTAIN)
	{
		BOTS_cp_console(clientNum, "Only the Captain can fire the scientist.");
		return;	
	}

	// find the scientist
	for (i = 0; i < level.maxclients; i++)
	{
		gentity_t* player = &g_entities[i];

		if (!player->client)
			continue;
		if (player->client->sess.sessionTeam == TEAM_SPECTATOR)
			continue;
		if (!OnSameTeam (ent, player))
			continue;
		if (player->bots_class != BOTS_SCIENTIST)
			continue;

		scientist = player;
		break;
	}

	if (!scientist)
	{
		BOTS_cp_console(clientNum, "Your team has no Scientist.");
		return;	
	}

	// tell him he got the boot and change him to a bodyguard
	BOTS_cp_console(scientist->s.clientNum, "YOU HAVE BEEN FIRED!!.");
	bots_ChangeClass ( scientist, BOTS_BODYGUARD );
}

void BOTS_scoutall(int clientNum, qboolean ab_captonly)
{
	gentity_t*	ent = g_entities + clientNum;
	int			i;
	int			teamdata[2][9];
	gentity_t*	player;
	char*		stats;

	if (!ent->client)
		return;

	if (ent->bots_class != BOTS_CAPTAIN)
	{
		BOTS_cp_console(clientNum, "Only the Captain can scout.");
		return;
	}

	for (i = 0; i < 9; i++)
	{
		teamdata[0][i] = 0;
		teamdata[1][i] = 0;
	}

	for (i = 0; i < level.maxclients; i++)
	{
		player = &g_entities[i];
		if (!player->client)
			continue;
		if (!player->inuse)
			continue;
		if (player->client->sess.sessionTeam == TEAM_SPECTATOR)
			continue;

		teamdata[player->client->sess.sessionTeam-1][player->bots_class-1]++;
	}

	stats =   va("Class           Female Male\n");
	stats = va("%s-----           ------ ----\n", stats);
	stats = va("%sCaptain         %3i    %3i \n", stats, teamdata[1][0], teamdata[0][0]);
	stats = va("%sBodyguard       %3i    %3i \n", stats, teamdata[1][1], teamdata[0][1]);
	stats = va("%sSniper          %3i    %3i \n", stats, teamdata[1][2], teamdata[0][2]);
	stats = va("%sSoldier         %3i    %3i \n", stats, teamdata[1][3], teamdata[0][3]);
	stats = va("%sBerzerker       %3i    %3i \n", stats, teamdata[1][4], teamdata[0][4]);
	stats = va("%sInfiltrator     %3i    %3i \n", stats, teamdata[1][5], teamdata[0][5]);
	stats = va("%sKamikazee       %3i    %3i \n", stats, teamdata[1][6], teamdata[0][6]);
	stats = va("%sNurse           %3i    %3i \n", stats, teamdata[1][7], teamdata[0][7]);
	stats = va("%sScientist       %3i    %3i \n", stats, teamdata[1][8], teamdata[0][8]);
	stats = va("bots_table \"%s\"", stats);

	if (ab_captonly) {
		trap_SendServerCommand( clientNum, stats);
	}
	else {
	for (i = 0; i < g_maxclients.integer; i++)
	{
		player = &g_entities[i];
		if (!player->client)
			continue; 
		if (!player->client->pers.scoutmsg)
			continue;
		if (player->client->sess.sessionTeam == TEAM_SPECTATOR)
			continue;
		if (!OnSameTeam (ent, player))
			continue;

			trap_SendServerCommand( player->s.clientNum, stats);
	}
}
}


void BOTS_killCam(int clientNum) {
	gentity_t*	ent = g_entities + clientNum;

	ent->client->sess.spectatorClient = 0;
	ent->cameralink = NULL;
}


void BOTS_viewCam(int clientNum) {

	gentity_t*	ent = g_entities + clientNum;
	gentity_t*	player = g_entities;
	qboolean	lb_found;
	int			i;

	// Find the first teammate at the right level
	lb_found = qfalse;
	for (i = 0; i < g_maxclients.integer; player++,i++)
	{
		if (!player->client)
			continue; 
		if (player->client->sess.sessionTeam == TEAM_SPECTATOR)
			continue;
		if (ent == player)
			continue;
		if (!OnSameTeam (ent, player))
			continue;

		lb_found = qtrue;
		break;
	}

	ent->client->sess.spectatorClient = player - g_entities;
	ent->cameralink = player;
}


void BOTS_limit(int clientNum)
{
	gentity_t*	ent = g_entities + clientNum;
	char		classname[MAX_TOKEN_CHARS];
	char		tmp[MAX_TOKEN_CHARS];
	int			limit;

	if (ent->bots_class != BOTS_CAPTAIN)
	{
		BOTS_cp_console(clientNum, "Only the Captain modify class limits.");
		return;
	}

	if (trap_Argc() > 1)
		trap_Argv(1, classname, sizeof(classname));

	if (trap_Argc() > 2)
		trap_Argv(2, tmp, sizeof(tmp));

	limit = atoi(tmp);

	if (Q_stricmp (classname, "bodyguard") == 0 )
		ent->client->pers.classLimit[BOTS_BODYGUARD] = limit;
	else if (Q_stricmp (classname, "sniper") == 0 )
		ent->client->pers.classLimit[BOTS_SNIPER] = limit;
	else if (Q_stricmp (classname, "soldier") == 0 )
		ent->client->pers.classLimit[BOTS_SOLDIER] = limit;
	else if (Q_stricmp (classname, "berzerker") == 0 )
		ent->client->pers.classLimit[BOTS_BERZERKER] = limit;
	else if (Q_stricmp (classname, "infiltrator") == 0 )
		ent->client->pers.classLimit[BOTS_INFILTRATOR] = limit;
	else if (Q_stricmp (classname, "kamikazee") == 0 )
		ent->client->pers.classLimit[BOTS_KAMIKAZEE] = limit;
	else if (Q_stricmp (classname, "nurse") == 0 )
		ent->client->pers.classLimit[BOTS_NURSE] = limit;
	else {
		BOTS_cp_console(clientNum, va("%s is not a valid class name.", classname));
		return;
	}

	BOTS_cp_console(clientNum, va("%s limit set to %i.", classname, limit));
}

void BOTS_video_setup(int clientNum)
{
	gentity_t*	ent = g_entities + clientNum;
	char*		list = NULL;
	char		netname[30];
	int			i, j;

	if (ent->bots_class != BOTS_CAPTAIN)
	{
		BOTS_cp_console(clientNum, "Only the Captain can use cameras.");
		return;
	}

	list = va("");

	for (i = 0, j = 0; i < level.maxclients; i++)
	{
		gentity_t* player = g_entities + i;

		if (!player->client)
			continue;
		if (ent == player)
			continue;
		if (!OnSameTeam (ent, player))
			continue;

		// remove the stupid ass colors losers use
		strcpy(netname, player->client->pers.netname);
		Q_CleanStr(netname);

		list = va("%s\\name%i\\%s", list, j, netname);
		j++;
	}
	trap_SetConfigstring(CS_BOTS_MENU + clientNum, va("size\\%i%s", j, list));

	trap_SendServerCommand(clientNum, "video_list_complete");

}

void BOTS_video(int clientNum)
{
	// gentity_t*	ent = g_entities + clientNum;
	char		fromUI[MAX_TOKEN_CHARS];
	int			i;
	char		netname[30];

	if (trap_Argc() > 1)
		trap_Argv( 1, fromUI, sizeof( fromUI ) );

	for (i = 0; i < level.maxclients; i++)
	{
		gentity_t* player = g_entities + i;

		if (!player->client)
			continue;

		// remove the stupid ass colors losers use
		strcpy(netname, player->client->pers.netname);
		Q_CleanStr(netname);

		if (Q_stricmp(fromUI, netname) != 0)
			continue;

		break;
	}
}

void BOTS_promote_setup(int clientNum)
{
	gentity_t*	ent = g_entities + clientNum;
	char*		list = NULL;
	int			i, j;
	int*		promotech;

	if (ent->bots_class != BOTS_CAPTAIN)
	{
		BOTS_cp_console(clientNum, "Only the Captain can promote.");
		return;
	}

	if (ent->promotionkey)
	{
		BOTS_cp_console(clientNum, "You don't have the pomotion key.");
		return;
	}

	promotech = BOTS_get_promotech_points(ent->bots_team);

	if (promotech[0] == 0)
	{
		BOTS_cp_console(clientNum, "You don't have any promotion points.");
		return;
	}

	list = va("");

	for (i = 0, j = 0; i < level.maxclients; i++)
	{
		gentity_t* player = g_entities + i;

		if (!player->client)
			continue;
		if (player == ent)
			continue;
		if (!OnSameTeam (ent, player))
			continue;
		if (player->client->pers.classlevel >= promotech[0])
			continue;
		if (player->client->pers.classlevel >= 4)
			continue;

		list = va("%s\\name%i\\%i", list, j, player->s.clientNum);
		j++;
	}

	if (j != 0) {
		trap_SetConfigstring(CS_BOTS_MENU + clientNum, va("size\\%i%s", j, list));
		trap_SendServerCommand(clientNum, "promote_list_complete");
	}
	else 
		BOTS_cp_console(clientNum, "No teammate(s) to promote.");
}

void BOTS_promote(int clientNum)
{
	gentity_t		*ent = g_entities + clientNum;
	char            fromUI[MAX_TOKEN_CHARS];
	int				*promotech;
	gentity_t		*player;

	
	if (!ent->client)
		return;
	
	if (trap_Argc() != 2)
		goto error;
	else {	
		char	*p = fromUI;
		int		i;
		
		trap_Argv( 1, fromUI, sizeof( fromUI ) );
		for (i=0; i<sizeof(fromUI); i++) {
			if (!*p)
				break;
			else
				if ((*p < '0') || (*p >'9'))
					goto error;
			p++;
		}
		if (p == fromUI)
			goto error;
	}

	promotech = BOTS_get_promotech_points(ent->bots_team);
	player = g_entities + atoi(fromUI);
	if ((ent == player) || (!player->client) || (player->bots_team != ent->bots_team) || (player->client->pers.classlevel >= 4) || (promotech[0] < (player->client->pers.classlevel+1)))
		goto error;

	player->client->pers.classlevel++;

	promotech[0] -= player->client->pers.classlevel;
	BOTS_set_promotech_points(ent->bots_team, promotech);
	
	BOTS_cp_console(player->s.clientNum, va("You have been promoted to level %i.", player->client->pers.classlevel));
	ClientUserinfoChanged( player->s.clientNum );
	return;

error:
	BOTS_cp_console(clientNum, "The promotion was aborted.");
}

void BOTS_demote_setup(int clientNum)
{
	gentity_t*	ent = g_entities + clientNum;
	char*		list = NULL;
	int			i, j;

	if (ent->bots_class != BOTS_CAPTAIN)
	{
		BOTS_cp_console(clientNum, "Only the Captain can demote.");
		return;
	}

	if (ent->promotionkey)
	{
		BOTS_cp_console(clientNum, "You don't have the pomotion key.");
		return;
	}

	list = va("");

	for (i = 0, j = 0; i < level.maxclients; i++)
	{
		gentity_t* player = g_entities + i;

		if (!player->client)
			continue;
		if (player == ent)
			continue;
		if (!OnSameTeam (ent, player))
			continue;
		if (player->client->pers.classlevel < 1)
			continue;

		list = va("%s\\name%i\\%i", list, j, player->s.clientNum);
		j++;
	}

	if (j != 0) {
		trap_SetConfigstring(CS_BOTS_MENU + clientNum, va("size\\%i%s", j, list));
		trap_SendServerCommand(clientNum, "demote_list_complete");
	}
	else 
		BOTS_cp_console(clientNum, "No teammate(s) to demote.");	
}

void BOTS_demote(int clientNum)
{
	gentity_t	*ent = g_entities + clientNum;
	char		fromUI[MAX_TOKEN_CHARS];
	int			*promotech;
	gentity_t	*player;


	if (!ent->client)
		return;

	if (trap_Argc() != 2)
		goto error;
	else {	
		char	*p = fromUI;
		int		i;

		
		trap_Argv( 1, fromUI, sizeof( fromUI ) );
		for (i=0; i<sizeof(fromUI); i++) {
			if (!*p)
				break;
			else
				if ((*p < '0') || (*p > '9'))
					goto error;
			p++;
		}
		if (p == fromUI)
			goto error;
	}

	player = g_entities + atoi(fromUI);
	if ((ent == player) || (!player->client) || (player->bots_team != ent->bots_team) || (player->client->pers.classlevel < 1))
		goto error;

	player->client->pers.classlevel--;

	promotech = BOTS_get_promotech_points(ent->bots_team);
	promotech[0]++;
	BOTS_set_promotech_points(ent->bots_team, promotech);

	BOTS_cp_console(player->s.clientNum, "You have been demoted.");
	ClientUserinfoChanged( player->s.clientNum );
	return;

error:
	BOTS_cp_console(clientNum, "The demotion was aborted.");
}

void BOTS_warcry(int clientNum)
{
	gentity_t	*ent = g_entities + clientNum;
	int			debounce, duration, i;
	char		*c;
	const float	
		fWARCRY_RADIUS = 400.0f,
		fWARCRY_MIN_FLAG_DISTANCE  = 800.0f;


	if (ent->bots_class != BOTS_CAPTAIN) {
		BOTS_cp_console(clientNum, "Only the Captain can shout like that.");
		return;
	}

	// find the flag, compute distance, check if that wuss is far enough from his own flag.
	switch (ent->client->sess.sessionTeam) {
		case TEAM_RED:
			c = "team_CTF_redflag";
			break;
		case TEAM_BLUE:
			c = "team_CTF_blueflag";
			break;		
		default:
			c = NULL;
	}

	if (c) {
		gentity_t *flag = G_Find (NULL, FOFS(classname), c);


		if ( flag && !(flag->flags & FL_DROPPED_ITEM)) {
			vec3_t v;

			VectorSubtract(ent->r.currentOrigin, flag->r.currentOrigin, v);
			if (VectorLength(v) < fWARCRY_MIN_FLAG_DISTANCE) {
				BOTS_cp_console(clientNum, "Don't hide behind your flag!");
				return;
			}
		}
	}

	// Enforce some delay
	if (ent->client->iWarcryDebounce > level.time) {
		BOTS_cp_console(clientNum, "*cough* *cough*");
		switch(ent->client->sess.sessionTeam) {
		case TEAM_RED:
			if (rand()&1)
				G_AddEvent( ent, EV_GENERAL_SOUND, G_SoundIndex("sound/cough_male.wav") );
			else
				G_AddEvent( ent, EV_GENERAL_SOUND, G_SoundIndex("sound/cough_male2.wav") );
			break;
		default:
		case TEAM_BLUE:
				G_AddEvent( ent, EV_GENERAL_SOUND, G_SoundIndex("sound/cough_female.wav"));
		}
		return;
	}

	// Yeah i'm too lazy to devise a numerical computation. Spaghetti code rulez!
	switch (ent->client->pers.classlevel) {
	default:
		;
	case 0:
		;
	case 1:
		debounce = 90;
		duration = 5;
	break;	

	case 2:
		debounce = 60;
		duration = 7;
	break;	

	case 3:
		debounce = 30;
		duration = 9;
	break;	

	case 4:
		debounce = 20;
		duration = 11;
	break;	
	}

	for (i=0; i<level.maxclients; i++)
	{
		gentity_t*	player = g_entities + i;
		vec3_t		v;


		if (!player->client)
			continue;
		if (player == ent)
			continue;
		if (!OnSameTeam (ent, player))
			continue;
		if (!visible(ent,player))
			continue;

		v[0] = ent->s.pos.trBase[0] - (player->s.pos.trBase[0] + (player->r.mins[0] + player->r.maxs[0])*0.5);
		v[1] = ent->s.pos.trBase[1] - (player->s.pos.trBase[1] + (player->r.mins[1] + player->r.maxs[1])*0.5);
		v[2] = ent->s.pos.trBase[2] - (player->s.pos.trBase[2] + (player->r.mins[2] + player->r.maxs[2])*0.5);

		if (VectorLength(v) > fWARCRY_RADIUS) {
			continue;
		}

		player->client->ps.powerups[PW_HASTE]	= level.time + duration*1000;	// Haste!
		player->client->ps.powerups[PW_WARCRY]	= level.time + duration*1000;	// 
		G_AddEvent(player, EV_POWERUP_WARCRY, 0);								// Eye candy
	}
	// Now for the all mighty captain himself.
	ent->client->iWarcryDebounce = level.time +  debounce*1000;
	G_AddEvent(ent, EV_WARCRY, 0);												// Eye candy
	// G_Printf(va("BOTS_warcry: lvl %i debounce %i\n",ent->client->pers.classlevel,debounce));
}
/////////////////////////////////////////////
//
// BOTS_CaptainCommands
//
// This will parse the passed cmd string and call to the correct functions.
// 
// RETURNS
//    qfalse : cmd is not an captain command
//	  qtrue	 : cmd is a captain command
//
qboolean BOTS_CaptainCommands(int clientNum, char* cmd)
{
	if (Q_stricmp (cmd, "droppromote") == 0 )
	{
		BOTS_droppromote(clientNum, qtrue);
		return qtrue;
	}
	else if (Q_stricmp (cmd, "firesci") == 0 )
	{
		BOTS_firesci(clientNum);
		return qtrue;
	}
	else if (Q_stricmp (cmd, "scoutall") == 0 )
	{
		BOTS_scoutall(clientNum, qfalse);
		return qtrue;
	}
	else if (Q_stricmp (cmd, "scout") == 0 )
	{
		BOTS_scoutall(clientNum, qtrue);
		return qtrue;
	}
	else if (Q_stricmp (cmd, "limit") == 0 )
	{
		BOTS_limit(clientNum);
		return qtrue;
	}
	else if (Q_strncmp (cmd, "video", strlen ("video")) == 0)
	{
		BOTS_video_setup(clientNum);
		return qtrue;
	}
	
	else if (Q_strncmp (cmd, "UI_to_server_video", strlen ("UI_to_server_video")) == 0)
	{
		BOTS_video(clientNum);
		return qtrue;
	}
	else if (Q_strncmp (cmd, "promote", strlen ("promote")) == 0)
	{
		BOTS_promote_setup(clientNum);
		return qtrue;
	}
	else if (Q_strncmp (cmd, "UI_to_server_promote", strlen ("UI_to_server_promote")) == 0)
	{
		BOTS_promote(clientNum);
		return qtrue;
	}
	else if (Q_strncmp (cmd, "demote", strlen ("demote")) == 0)
	{
		BOTS_demote_setup(clientNum);
		return qtrue;
	}
	else if (Q_strncmp (cmd, "UI_to_server_demote", strlen ("UI_to_server_demote")) == 0 )
	{
		BOTS_demote(clientNum);
		return qtrue;
	}
	else if (Q_strncmp (cmd, "warcry", strlen ("warcry")) == 0 )
	{
		BOTS_warcry(clientNum);
		return qtrue;
	}

	return qfalse;
}

/////////////////////////////////////////////
//
// BOTS_ClientThink_captain
//
// Captain think routine.  Called once for each client think if
// the client is a captain.
//
void BOTS_ClientThink_captain(gentity_t* ent)
{
	// promo key distance
	if (ent->promotionkey)
	{
		vec3_t	dist1;
		int		len;
		float	lf_lostkey;

		VectorSubtract(ent->s.pos.trBase, ent->promotionkey->s.pos.trBase, dist1);
		len = VectorLength(dist1);
		bots_updateKeyDistanceCaptain(ent,(short)len);

		lf_lostkey = bots_getCaptainLostKey(ent->bots_team);
		if ((lf_lostkey) && ((lf_lostkey + 3000) < level.time)) {
			bots_setCaptainLostKey(ent->bots_team, qtrue);
			BOTS_touch_promo_key(ent->promotionkey, ent);
			BOTS_cp_console(ent->s.clientNum, "Your Promotion Key has been returned!");
		}
	}
	else
		bots_updateKeyDistanceCaptain(ent,0);

	if (ent->health <= 0)
		return;
}
